﻿using System;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;

namespace OpenLS.Spreadsheet.Formats.Biff.Records
{


    internal struct AFDOper
    {
        internal static class VtValueBase
        {


            internal static object Read(AFDOper.VtValueType type, BinaryReader reader)
            {
                switch (type)
                {
                    case AFDOper.VtValueType.Ignored:
                        {
                            reader.ReadBytes(8);
                            return null;
                        }
                    case AFDOper.VtValueType.AFDOperBoolErr:
                        return new AFDOperBoolErr(reader);
                    case AFDOper.VtValueType.AFDOperRk:
                        return new AFDOperRk(reader);
                    case AFDOper.VtValueType.AFDOperStr:
                        return new AFDOperStr(reader, true, false);
                    case AFDOper.VtValueType.AllBlanksMatched:
                        return new AllBlanksMatched(reader);
                    case AFDOper.VtValueType.AllNonBlanksMatched:
                        return new AllNonBlanksMatched(reader);
                    case AFDOper.VtValueType.Xnum:
                        return reader.ReadDouble();
                    default: throw new NotSupportedException();
                }
            }

        }


        internal class AllNonBlanksMatched 
        {
            public AllNonBlanksMatched(BinaryReader reader)
            {
                reader.ReadUInt64();
            }


        }

        internal class AllBlanksMatched 
        {
            public AllBlanksMatched(BinaryReader reader)
            {
                reader.ReadUInt64();
            }

        }

        internal class AFDOperStr 
        {
            public byte cch;
            private uint unused3;
            private bool fCompare;
            private byte reserved1;
            private byte unused2;

            public AFDOperStr(BinaryReader reader, bool includeUnused, bool partOfAutoFilter12)
            {
                if (includeUnused)
                    reader.ReadUInt32();
                this.cch = reader.ReadByte();
                this.fCompare = reader.ReadByte() != 0;
                this.reserved1 = reader.ReadByte();
                this.unused2 = reader.ReadByte();
                if (partOfAutoFilter12)
                this.unused3 = reader.ReadUInt32();

            }

            public AFDOperStr(int count)
            {
                this.cch = (byte) count;
            }

            public void Write(BinaryWriter writer, bool includeUnused, bool partOfAutoFilter12)
            {
                if (includeUnused)
                    writer.Write((uint)0);
                writer.Write(cch);
                writer.Write((byte)(fCompare?1:0));
                writer.Write(reserved1);
                writer.Write(unused2);
                if (partOfAutoFilter12)
                    writer.Write(unused3);

            }
        }

        internal class AFDOperRk 
        {
            private RKNumber _rkNumber;
            public AFDOperRk(BinaryReader reader)
            {
                _rkNumber.Read(reader);
                reader.ReadUInt32();
            }

        }

        internal class AFDOperBoolErr 
        {
            private Bes _Bes;
            public AFDOperBoolErr(BinaryReader reader)
            {
                this._Bes.Read(reader);
                reader.ReadUInt16();
                reader.ReadUInt32();
            }

        }

       // public VtValueType vt;
        public grbitSignType grbitSign;
        public object vtValue;
        internal VtValueType vt;

        public AFDOper(CustomFilter customFilter, out string s)
        {
            if (customFilter == null)
            {
                this.grbitSign = grbitSignType.Undefined;
                this.vt = VtValueType.Ignored;
                this.vtValue = null;
                s = null;
                return;
            }
            s = null;
            switch (customFilter.Operator)
            {
                case FilterOperator.Equal: this.grbitSign = grbitSignType.EqualTo;
                    break;
                case FilterOperator.NotEqual: this.grbitSign = grbitSignType.NotEqual;
                    break;
                case FilterOperator.LessThan: this.grbitSign = grbitSignType.LessThan;
                    break;
                case FilterOperator.LessThanOrEqual: this.grbitSign = grbitSignType.LessThanOrEqual;
                    break;
                case FilterOperator.GreaterThan: this.grbitSign = grbitSignType.GreaterThan;
                    break;
                case FilterOperator.GreaterThanOrEqual: this.grbitSign = grbitSignType.GreaterThanOrEqual;
                    break;
                default: grbitSign = grbitSignType.Undefined;
                    break;
            }
            double d;
            if (double.TryParse(customFilter.Value, out d))
            {
                this.vt = VtValueType.Xnum;
                this.vtValue = d;
                return;
            }
            else if (customFilter.Value != null)
            {
                s = customFilter.Value;
                vt = VtValueType.AFDOperStr;
                vtValue = new AFDOperStr(s.Length);
            }
            else
            {
                vt = VtValueType.Ignored;
                s = null;
                vtValue = null;
            }
        }

        public void Read(BinaryReader reader)
        {
            this.vt = (VtValueType) reader.ReadByte();
            this.grbitSign = (grbitSignType) reader.ReadByte();
            this.vtValue = VtValueBase.Read(vt, reader);
        }

        internal enum VtValueType : byte
        {
            Ignored = 0x0,
            AFDOperRk = 0x02,
            Xnum = 0x04,
            AFDOperStr = 0x06,
            AFDOperBoolErr = 0x08,
            AllBlanksMatched = 0x0C,
            AllNonBlanksMatched = 0x0E,
        }

        public enum grbitSignType: byte
        {
            Undefined = 0x0,
            LessThan = 0x01,
            EqualTo = 0x02,
            LessThanOrEqual = 0x03,
            GreaterThan = 0x04,
            NotEqual = 0x05,
            GreaterThanOrEqual = 0x06,
        }

        public void Write(BinaryWriter writer)
        {
            writer.Write((byte)vt);
            writer.Write((byte)grbitSign);
            switch (vt)
            {
                    case VtValueType.AFDOperBoolErr:
                    throw new NotImplementedException();
                    case VtValueType.AFDOperRk:
                        throw new NotImplementedException();
                    case VtValueType.AFDOperStr:
                        {
                            AFDOperStr s = (AFDOperStr) vtValue;
                            s.Write(writer, true, false);
                        }
                    break;
                case VtValueType.AllBlanksMatched:
                        writer.Write((ulong)0);
                    break;
                case VtValueType.AllNonBlanksMatched:
                        writer.Write((ulong)0);
                    break;
                case VtValueType.Ignored:
                        writer.Write(new byte[8]);
                    break;
                case VtValueType.Xnum:
                    double d = (double) vtValue;
                    writer.Write(d);
                    break;
                default: throw new NotSupportedException();
            }
        }

        public CustomFilter ToCustomFilter(string str)
        {
            if (grbitSign == grbitSignType.Undefined)
                return null;
            CustomFilter result = new CustomFilter();
            switch (this.grbitSign)
            {
                    case grbitSignType.EqualTo: result.Operator = FilterOperator.Equal;
                    break;
                case grbitSignType.GreaterThan: result.Operator = FilterOperator.GreaterThan;
                    break;
                case grbitSignType.GreaterThanOrEqual: result.Operator = FilterOperator.GreaterThanOrEqual;
                    break;
                case grbitSignType.LessThan: result.Operator = FilterOperator.LessThan;
                    break;
                case grbitSignType.LessThanOrEqual: result.Operator = FilterOperator.LessThanOrEqual;
                    break;
                case grbitSignType.NotEqual:result.Operator = FilterOperator.NotEqual;
                    break;
                default: throw new NotSupportedException();
            }
            switch (this.vt)
            {
                case VtValueType.Xnum:
                    result.Value = this.vtValue.ToString();
                    break;
                case VtValueType.AFDOperStr:
                    Contract.Assert(str != null);
                    result.Value = str;
                    break;
                case VtValueType.Ignored:
                    break;
                case VtValueType.AFDOperBoolErr:
                    case VtValueType.AFDOperRk:
                    case VtValueType.AllBlanksMatched:
                    case VtValueType.AllNonBlanksMatched:
                    throw new NotImplementedException();
                default: throw new NotSupportedException();
            }
            return result;
        }
    }

    internal class Bes
    {
        public void Read(BinaryReader reader)
        {
            throw new NotImplementedException();
        }
    }
}